#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#if TARGET_OS_IPHONE
#import <CFNetwork/CFNetwork.h>
#endif

/* Port to listen on */
#define PORT 2048

void AcceptCallBack(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);

int main(int argc, char **argv) 
{
  /* The server socket */
  CFSocketRef TCPServer;

  /* The punchline to our joke */
  char punchline[] = "To get to the other side! Ha ha!\n\r";

  /* Used by setsockopt */
  int yes = 1;

  /* Build our socket context; this ties the punchline to the socket */
  CFSocketContext CTX = { 0, punchline, NULL, NULL, NULL };

  /* Create the server socket as a TCP IPv4 socket and set a callback */
  /* for calls to the socket's lower-level accept() function */
  TCPServer = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP,
  kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
  if (TCPServer == NULL)
    return EXIT_FAILURE;

  /* Re-use local addresses, if they're still in TIME_WAIT */
  setsockopt(CFSocketGetNative(TCPServer), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));

  /* Set the port and address we want to listen on */
  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
  NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
  if (CFSocketSetAddress(TCPServer, (CFDataRef) address) != kCFSocketSuccess)  
  {
    fprintf(stderr, "CFSocketSetAddress() failed\n");
    CFRelease(TCPServer);
    return EXIT_FAILURE;
  }

  CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, TCPServer, 0);
  CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
  CFRelease(sourceRef);

  printf("Socket listening on port %d\n", PORT);

  CFRunLoopRun();

  /* Get on with our life, instead of waiting for incoming connections. */
  while(1) 
  {
    struct timeval tv;
    tv.tv_usec = 0;
    tv.tv_sec = 1;
    select(-1, NULL, NULL, NULL, &tv);
  }  
}
void AcceptCallBack(
    CFSocketRef socket,
    CFSocketCallBackType type,
    CFDataRef address,
    const void *data,
    void *info)
{
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    CFIndex bytes;
    UInt8 buffer[128];
    UInt8 recv_len = 0, send_len = 0;

    /* The native socket, used for various operations */
    CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;

    /* The punch line we stored in the socket context */
    char *punchline = info;

    /* Create the read and write streams for the socket */
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock, &readStream, &writeStream);

    if (!readStream || !writeStream) 
    {
        close(sock);
        fprintf(stderr, "CFStreamCreatePairWithSocket() failed\n");
        return;
    }

    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);

    /* Wait for the client to finish sending the joke; wait for newline */
    memset(buffer, 0, sizeof(buffer));
    while (!strchr((char *) buffer, '\n') && recv_len < sizeof(buffer)) 
    {
        bytes = CFReadStreamRead(readStream, buffer + recv_len, sizeof(buffer) - recv_len);
        if (bytes < 0) 
        {
            fprintf(stderr, "CFReadStreamRead() failed: %d\n", bytes);
            close(sock);
            return;
        }
        recv_len += bytes;
    }

    /* Send the punchline */
    while (send_len < (strlen(punchline+1))) 
    {
        if (CFWriteStreamCanAcceptBytes(writeStream)) {
            bytes = CFWriteStreamWrite(writeStream,
                (unsigned char *) punchline + send_len,
                (strlen((punchline)+1) - send_len) );
            if (bytes < 0) {
                fprintf(stderr, "CFWriteStreamWrite() failed\n");
                close(sock);
                return;
            }
            send_len += bytes;
        }
        close(sock);
        CFReadStreamClose(readStream);
        CFWriteStreamClose(writeStream);
        return;
    }
}
